/**
 * FileName: PolygonClipUtil.js
 * Author:   chenhao
 * Date:     2022/6/30 14:47
 * Description:
 */

/**
 * 多边形切割工具
 */
const PolygonClipUtil = {
  /**
   * 合并多边形
   */
  unionPolygon: function (polygons) {
    let polygon = polygons[0];
    for (let i = 0; i < polygons.length; i++) {
      polygon = turf.union(polygon, polygons[i]);
    }
    return polygon;
  },

  /**
   * 线分割面
   * 面类型只能是polygon或是环
   * 线与多边形必须有两个交点
   */
  polygonClipByLine: function (polygon, clipLine) {
    if (polygon.geometry.type === 'Polygon') {
      let polyLine = turf.polygonToLine(polygon);
      if (polyLine.geometry.type === 'LineString') { // 切割普通多边形
        return this.singlePolygonClip(polyLine, clipLine);
      } else if (polyLine.geometry.type === 'MultiLineString') { //切割环
        return this.multiPolygonClip(polyLine, clipLine);
      }
    } else if (polygon.geometry.type === 'MultiPolygon') {
      // 若输入的多边形类型为Multipolygon则拆分成多个Polygon
      let polygons = this.multiPolygon2polygons(polygon);
      let clipPolygon = null;
      let clipPolygonIndex = -1;
      // 获取MultiPolygon中与切割线相交的多边形（有且只能有一个多边形相交2个交点）
      polygons.forEach(function (polygon, index) {
        let polyLine = turf.polygonToLine(polygon);
        if (turf.lineIntersect(polyLine, clipLine).features.length === 2) {
          if (!clipPolygon) {
            clipPolygon = polygon;
            clipPolygonIndex = index;
          } else {
            return {state: 'error', message: 'MultiPolygon只能有一个多边形与切割线存在交点'};
          }
        }
      });
      if (clipPolygonIndex !== -1) {
        polygons.splice(clipPolygonIndex, 1);
        return turf.featureCollection(polygons.concat(this.polygonClipByLine(clipPolygon, clipLine).features));
      } else {
        return {state: 'error', message: 'MultiPolygon与切割线无交点'};
      }

    } else {
      return {state: 'error', message: '输入的多边形类型为错误'};
    }
  },

  /**
   * 线分割面
   * 切割普通多边形
   */
  singlePolygonClip: function (polyLine, clipLine) {
    // 获得裁切点
    let intersects = turf.lineIntersect(polyLine, clipLine);
    if (intersects.features.length !== 2) {
      return {state: 'error', message: '切割线与多边形交点应该为2个,当前交点个数为' + intersects.features.length};
    }
    // 检查切割线与多边形的位置关系 （切割线的起点和终点不能落在多边形内部）
    let clipLineLength = clipLine.geometry.coordinates.length;
    let clipLineStartPoint = turf.point(clipLine.geometry.coordinates[0]);
    let clipLineEndPoint = turf.point(clipLine.geometry.coordinates[clipLineLength - 1]);
    let polygon = turf.polygon([polyLine.geometry.coordinates]);
    if (turf.booleanPointInPolygon(clipLineStartPoint, polygon) || turf.booleanPointInPolygon(clipLineEndPoint, polygon)) {
      return {state: 'error', message: '切割线起点或终点不能在 裁剪多边形内部'};
    }
    // 通过裁切点 分割多边形（只能获得多边形的一部分）
    let slicedPolyLine = turf.lineSlice(intersects.features[0], intersects.features[1], polyLine);
    // 裁剪线分割 保留多边形内部部分
    let slicedClipLine = turf.lineSlice(intersects.features[0], intersects.features[1], clipLine);
    // 重新拼接多边形 存在 对接的问题 所以先进行判断 如何对接裁剪的多边形和裁剪线
    let resultPolyline1 = this.connectLine(slicedPolyLine, slicedClipLine);
    resultPolyline1.properties.clipName = 'clip_01';
    // 闭合线 来构造多边形
    resultPolyline1.geometry.coordinates.push(resultPolyline1.geometry.coordinates[0]);
    let resultPolygon1 = turf.lineToPolygon(resultPolyline1);
    // 构造切割的另一面多边形
    let firstPointOnLine = this.isOnLine(turf.point(polyLine.geometry.coordinates[0]), slicedPolyLine);
    let pointList = [];
    if (firstPointOnLine) {
      for (let i = 0; i < polyLine.geometry.coordinates.length; i++) {
        let coordinate = polyLine.geometry.coordinates[i];
        if (!this.isOnLine(turf.point(coordinate), slicedPolyLine)) {
          pointList.push(coordinate);
        }
      }
    } else {
      let skipNum = 0; // 记录前面被跳过的点的个数
      let isStartPush = false;
      for (let i = 0; i < polyLine.geometry.coordinates.length; i++) {
        let coordinate = polyLine.geometry.coordinates[i];
        if (!this.isOnLine(turf.point(coordinate), slicedPolyLine)) {
          if (isStartPush) {
            pointList.push(coordinate);
          } else {
            skipNum++;
          }

        } else {
          isStartPush = true;
        }
      }
      // 将前面跳过的点补充到 点数组中
      for (let i = 0; i < skipNum; i++) {
        pointList.push(polyLine.geometry.coordinates[i]);
      }
    }
    let slicedPolyLine_2 = turf.lineString(pointList, slicedPolyLine.properties);
    let resultPolyline2 = this.connectLine(slicedPolyLine_2, slicedClipLine);
    resultPolyline2.properties.clipName = 'clip_02';
    // 闭合线 来构造多边形
    resultPolyline2.geometry.coordinates.push(resultPolyline2.geometry.coordinates[0]);
    let resultPolygon2 = turf.lineToPolygon(resultPolyline2);
    // 返回面要素集
    return {
      features: turf.featureCollection([
        resultPolygon1,
        resultPolygon2
      ]), state: 'success'
    };

  },

  /**
   * 线分割面
   * 切割环
   */
  multiPolygonClip: function (polyLine, clipLine) {
    // 将环 多边形分割成 内部逆时针多边形+外部多边形
    let outPolyline, insidePolylineList = [];
    for (let i = 0; i < polyLine.geometry.coordinates.length; i++) {
      let splitPolyline = turf.lineString(polyLine.geometry.coordinates[i]);
      if (turf.booleanClockwise(splitPolyline)) {
        if (outPolyline) {
          return {state: 'error', message: '出现了两个外部多边形无法处理'};
        } else {
          outPolyline = splitPolyline;
        }
      } else {
        let intersects = turf.lineIntersect(splitPolyline, clipLine);
        if (intersects.features.length > 0) {
          return {state: 'error', message: '切割线不能与内环有交点'};
        }
        insidePolylineList.push(splitPolyline);
      }
    }
    let resultCollection = this.singlePolygonClip(outPolyline, clipLine);

    for (let i = 0; i < resultCollection.features.length; i++) {
      for (let j = 0; j < insidePolylineList.length; j++) {
        let startPoint = turf.point(insidePolylineList[j].geometry.coordinates[0]);
        if (turf.booleanPointInPolygon(startPoint, resultCollection.features[i])) {
          resultCollection.features[i] = turf.mask(resultCollection.features[i], turf.lineToPolygon(insidePolylineList[j]));
        }
      }
    }
    return resultCollection;
  },

  /**
   * 连接两条线
   * 方法会将两条线段最近的一段直接连接
   */
  connectLine: function (line1, line2) {
    let line2_length = line2.geometry.coordinates.length;
    let line1_startPoint = line1.geometry.coordinates[0];
    let line2_startPoint = line2.geometry.coordinates[0];
    let line2_endPoint = line2.geometry.coordinates[line2_length - 1];
    let pointList = [];
    // 获取line1 所有点坐标
    for (let i = 0; i < line1.geometry.coordinates.length; i++) {
      let coordinate = line1.geometry.coordinates[i];
      pointList.push(coordinate);
    }

    // 判断两条线的 起点是否接近，如果接近 逆转line2线 进行连接
    if (turf.distance(line1_startPoint, line2_startPoint) < turf.distance(line1_startPoint, line2_endPoint)) {
      line2.geometry.coordinates = line2.geometry.coordinates.reverse();
    }
    for (let i = 0; i < line2.geometry.coordinates.length; i++) {
      let coordinate = line2.geometry.coordinates[i];
      pointList.push(coordinate);
    }
    return turf.lineString(pointList, line1.properties);
  },

  /**
   * 判断点是否在线里面
   * 注：线组成的坐标对比
   */
  isOnLine: function (point, line) {
    for (let i = 0; i < line.geometry.coordinates.length; i++) {
      let coordinate = line.geometry.coordinates[i];
      if (point.geometry.coordinates[0] === coordinate[0] && point.geometry.coordinates[1] === coordinate[1]) {
        return true;
      }
    }
    return false;
  },

  /**
   * 获得两条线交点
   */
  getIntersectPoints: function (line1, line2) {
    return turf.lineIntersect(line1, line2);
  },

  /**
   * multiPolygon转polygons
   */
  multiPolygon2polygons: function (multiPolygon) {
    if (multiPolygon.geometry.type !== 'MultiPolygon') {
      return;
    }
    let polygons = [];
    multiPolygon.geometry.coordinates.forEach((item) => {
      let polygon = {
        type: 'Feature',
        properties: {},
        geometry: {
          type: 'Polygon',
          coordinates: []
        }
      };
      polygon.geometry.coordinates = item;
      polygons.push(polygon);
    });
    return polygons;
  },

  /**
   * polygons转multiPolygon
   * 考虑polygons中就存在多面的情况
   */
  polygons2MultiPolygon: function (geoJson) {
    let newGeoJson = {
      type: 'FeatureCollection',
      features: [{geometry: {coordinates: [], type: 'MultiPolygon'}, type: 'Feature', properties: {}}]
    };
    geoJson.features.forEach((item) => {
      if (item.geometry.type === 'Polygon') {
        newGeoJson.features[0].geometry.coordinates.push(item.geometry.coordinates);
      } else {
        item.geometry.coordinates.forEach((item) => {
          newGeoJson.features[0].geometry.coordinates.push(item);
        });
      }
    });
    return newGeoJson;
  },
};
export default PolygonClipUtil;
